import LinkListNode, {LinkList} from "./LinkList";
import Queue from "./Queue";

export default class GameFrameworkLinkedList<T> {
    private readonly _linkedList: LinkList<T>;
    private readonly _cachedNodes: Queue<LinkListNode<T>>;

    public constructor() {
        this._linkedList = new LinkList<T>()
        this._cachedNodes = new Queue<LinkListNode<T>>();
    }

    get Count() {
        return this._linkedList.Size();
    }

    get CachedNodeCount() {
        return this._cachedNodes.Size();
    }

    get First() {
        return this._linkedList.First;
    }

    get Last() {
        return this._linkedList.Last;
    }

    AddAfter(node: LinkListNode<T>, value: T): LinkListNode<T> {
        let newnode: LinkListNode<T> = this.AcquireNode(value);
        this._linkedList.AddAfter2(node, newnode);
        return newnode;
    }

    AddAfter2(node: LinkListNode<T>, newnode: LinkListNode<T>): void {
        this._linkedList.AddAfter2(node, newnode);
    }

    AddBefore(node: LinkListNode<T>, value: T): LinkListNode<T> {
        let newnode: LinkListNode<T> = this.AcquireNode(value);
        this._linkedList.AddBefore2(node, newnode);
        return newnode;
    }

    AddBefore2(node: LinkListNode<T>, newnode: LinkListNode<T>): void {
        this._linkedList.AddBefore2(node, newnode);
    }

    AddFirst(value: T): LinkListNode<T> {
        let newnode: LinkListNode<T> = this.AcquireNode(value);
        this._linkedList.AddFirst2(newnode);
        return newnode;
    }

    AddFirst2(newnode: LinkListNode<T>): void {
        this._linkedList.AddFirst2(newnode);
    }

    AddLast(value: T): LinkListNode<T> {
        let newnode: LinkListNode<T> = this.AcquireNode(value);
        this._linkedList.AddLast2(newnode);
        return newnode;
    }

    AddLast2(newnode: LinkListNode<T>): void {
        this._linkedList.AddLast2(newnode);
    }

    ForEach(func:(value:T)=>boolean):T{
        return this._linkedList.ForEach(func);
    }


    public Clear(): void {
        let current: LinkListNode<T> = this._linkedList.First;
        while (current != null) {
            this.ReleaseNode(current);
            current = current.Next;
        }

        this._linkedList.Clear();
    }

    ClearCachedNodes(){
        this._cachedNodes.Clear();
    }

    Contains(value:T):boolean{
        return this._linkedList.Contains(value);
    }

    Find(value:T):LinkListNode<T>{
        return this._linkedList.Find(value);
    }

    FindLast(value:T):LinkListNode<T>{
        return this._linkedList.FindLast(value);
    }

    Remove(value:T):boolean{
        let node:LinkListNode<T> = this._linkedList.Find(value);
        while (node != null){
            this._linkedList.Remove(value);
            this.ReleaseNode(node);
            return true;
        }
        return false;
    }
    Remove2(node:LinkListNode<T>):void{
        this._linkedList.Remove2(node);
        this.ReleaseNode(node);
    }

    RemoveFirst():void{
        let first:LinkListNode<T> = this._linkedList.First;
        if (first == null)
        {
            throw new Error("First is invalid.");
        }

        this._linkedList.RemoveFirst();
        this.ReleaseNode(first);
    }

    RemoveLast():void{
        let first:LinkListNode<T> = this._linkedList.First;
        if (first == null)
        {
            throw new Error("Last is invalid.");
        }

        this._linkedList.RemoveLast();
        this.ReleaseNode(first);
    }

    private AcquireNode(value: T): LinkListNode<T> {
        let node: LinkListNode<T> = null;
        if (this._cachedNodes.Size() > 0) {
            node = this._cachedNodes.dequeue();
            node.Value = value;
        } else {
            node = new LinkListNode<T>(null, value);
        }
        return node;
    }

    private ReleaseNode(node: LinkListNode<T>): void {
        node.Value = null;
        this._cachedNodes.enqueue(node);
    }

}